///////////////////////////////////////////////////////////
///////////////// Have an itch? Scratch it! ///////////////
///////////////////////// SCRATCH /////////////////////////
/////////////////////  A MUD  Server   ////////////////////
///////////////////// By: Jared Devall ////////////////////
/////////////////////      Thanks:     ////////////////////
/////////////////////  DIKU/Merc/ROM   ////////////////////
///////////////////// Aetas/Deus Gang  ////////////////////
/////////////////////       Beej       ////////////////////
///////////////////////////////////////////////////////////


#include <cstdarg>
#include <cstdio>
#include <fstream>
#include <map>
#include <mysql/mysql.h>
#include "smysql.h"
#include "avatar.h"
#include "colorTable.h"
#include "stringutil.h"
#include "timestamp.h"

using namespace std;

Avatar::Avatar( ) {
	_disconnected = false;
	_socket = new Socket;
	_gotInput = false;
	_status = LOGIN;
}

// get rid of the socket when avatar is deleted
// disconnect and remove from list & delete
Avatar::~Avatar( ) {
	if ( _socket ) {
		if ( !_socket->GetSocketIP().empty() ) {
			SocketServer::Instance()._socketList.remove( _socket );
			SocketServer::Instance().KillSocket( _socket ); 
			if ( Get( "name" ).empty() ) {
				cout << _socket->GetSocketIP() << " has disconnected @ " << Timestamp::Instance().GetDateTime() << "\n\r";
			} else {
				cout << Get( "name" ) << " has disconnected @ " << Timestamp::Instance().GetDateTime() << "\n\r";
			}
		}
		delete _socket;
	}
}

bool Avatar::Load( ) {	
	string query;
	Mysql::LMAP::iterator avatar_info;

	query << "SELECT * FROM user WHERE username='" << Mysql::Instance().EscapeString( Get( "name" ) ) << "'";
	
	if ( !Mysql::Instance().Load( query.c_str() ) ) 
		return false;	
   
   avatar_info = Mysql::Instance()._info[0].find("username");
   Set( "name", avatar_info->second );
   avatar_info = Mysql::Instance()._info[0].find("password");
   Set( "password", avatar_info->second );
   avatar_info = Mysql::Instance()._info[0].find("_group");
   Set( "group", avatar_info->second );
   avatar_info = Mysql::Instance()._info[0].find("reply");
   Set( "reply", avatar_info->second );
   avatar_info = Mysql::Instance()._info[0].find("title");
   Set( "title", avatar_info->second );
   avatar_info = Mysql::Instance()._info[0].find("login");
   Set( "login", avatar_info->second );
   avatar_info = Mysql::Instance()._info[0].find("prompt");
   Set( "prompt", avatar_info->second );
   
   	
   return true;
}

bool Avatar::Save( ) {
	string query;

	if ( Get( "group" ).empty() ) {
    	Create();
    	return true;
    }

	// We have to escape certain strings because they may possibly have a single quote in them.
	query << "UPDATE user SET username='" << Mysql::Instance().EscapeString( Get( "name" ) ) << "', password='" << Mysql::Instance().EscapeString( Get( "password" ) ) << "', _group='" << Get( "group" ) << "',";
	query << " login='" << Mysql::Instance().EscapeString( Get( "login" ) ) << "', reply='" << Mysql::Instance().EscapeString( Get( "reply" ) ) << "', title='" << Mysql::Instance().EscapeString( Get( "title" ) ) << "',";
	query << " prompt='" << Mysql::Instance().EscapeString( Get( "prompt" ) ) << "' WHERE username='" << Mysql::Instance().EscapeString( Get( "name" ) ) << "'";	

    Mysql::Instance().RealQuery( query.c_str() );
    return true;
}

bool Avatar::Create( ) {
	string query;
	
	// We have to escape the name because it might have a single quote in it.. '
	query << "INSERT INTO user ( username, password ) VALUES ( '" << Mysql::Instance().EscapeString( Get( "name" ) ) << "', '" << Get( "password" ) << "' )";   
	
    Mysql::Instance().Query( query.c_str() );
    return true;
}

bool Avatar::Delete( ) {
	string query;
	
	query << "DELETE FROM user WHERE username='" << Mysql::Instance().EscapeString( Get( "name" ) ) << "'";	
	
	Mysql::Instance().Query( query.c_str() );
	return true;
}

STATUS Avatar::GetStatus( ) {
	return _status;
}

void Avatar::SetStatus( STATUS status ) {
	_status = status;
}

void Avatar::Send( char * message, ... ) {
	va_list args;
	char buf[MAX_BUFFER];
	string buf2;

    va_start( args, message );

	vsprintf( buf, message, args);
	buf2 = _socket->GetOutput();
	buf2.append( buf );
	
	_socket->SetOutput( buf2 );
	va_end( args );
}

void Avatar::Send( const string &message ) {
	string buf;

	buf.append( message );	
	_socket->SetOutput( buf );
}

void Avatar::SetSocket( Socket * socket ) {
	_socket = socket;
}

Socket * Avatar::GetSocket( ) {
	return _socket;
}

bool Avatar::SetName( const string &name ) {
	string buf;
	
	// Validate name
	if ( name.length() < 3 || name.length() > 15 ) {
		buf << name.length();
		return false;
	}


	buf << name;

	for ( string::iterator cChar = buf.begin(); cChar != buf.end(); ++cChar ) {
		if ( !isalnum(*cChar) && (*cChar) != '\'' )
			return false;
	}
	
	// Name is good
	Set( "name", name );
	return true;
}

bool Avatar::VerifyPassword( const string &password ) {	
	return Get( "password" ) == EncryptPassword( password );
}	

string Avatar::EncryptPassword( const string &password ) {
	return crypt( password.c_str(), "$1$zetyadr" );
}

void Avatar::ClearPassword( ) {
	string blah = "e"; blah.erase();
	Set( "password", blah );
}

bool Avatar::Set( const string &set, const string &value ) {
	string buf;	
	
	if ( !strcasecmp( set, "title" ) ) {
		if ( value[0] != ',' && value[0] != '\'' && value[0] != ' ' ) {
			buf << " " << value;
   	 } else {
	  	 buf << value;
   	 }
		_info[set] = buf;
	} else if ( !strcasecmp( set, "name" ) ) {
		string buf;
	
		// Validate name
		if ( value.length() < 3 || value.length() > 15 ) {
			buf << value.length();
			Send( "Names must be 3-15 characters in length.\n\r" );
			return false;
		}


		buf << value;

		for ( string::iterator cChar = buf.begin(); cChar != buf.end(); ++cChar ) {
			if ( !isalnum(*cChar) && (*cChar) != '\'' ) {
				Send( "Names may only contain: alpha-numeric '\n\r" );
				return false;
			}
		}
		_info[set] = buf;
	} else if ( !strcasecmp( set, "password" ) ) {
		_info[set] = value;
	} else {
		_info[set] = value;
	}
	return true;
}

string Avatar::Get( const string &get ) {
	map< string, string >::iterator info;
	
		
	return _info[get];
}
	
void Avatar::SetEdit( const string &edit ) {
	if ( edit.empty() ) {
		_edit.erase();
	} else {
		_edit = edit;
	}
}

string Avatar::GetEdit( ) {
	return _edit;
}

bool Avatar::HasPassword( ) {
	return Get( "password" ).empty();
}
void Avatar::SetGotInput( bool value ) {
	_gotInput = value;
}

bool Avatar::GotInput( ) {
	return _gotInput;
}

void Avatar::FlushOutput( ) {
	string output;
	string buf2;
	string buf3;
	string colorClear;
	// This will add a prompt to the end of output
	// Ofcourse, one needs some text before it'll add a prompt
	// or we can add a prompt if we get some input and it calls for it. :)
	
	output = _socket->GetOutput();
	if ( output.empty() != true || _gotInput ) {
		if ( output.find( string(_handlers.front()->Prompt( this ) )) != string::npos )
			return;
			
		//if ( this->waiting == false )  { // Don't send a prompt if there's delayed text going
			if ( !_gotInput ) {
					output.insert( 0, "\n\r" );
			} else {
				// Only if there's no input set gotInput to false
				// we do it like this so there isn't an extra newline
				// everytime you get some output
				if ( _socket->GetInput().empty() )
					_gotInput = false;						
			}
			output.append( "\n\r" + _handlers.front()->Prompt( (this) ) );			
			
			_socket->SetOutput( output );
			
		//}
	}
	
	// This parses our color.
	colorClear << ColorTable::COLOR_ESCAPE << ColorTable::COLOR_CLEAR;
	buf3 = _socket->GetOutput();
	buf3.append( colorClear );
	
	for ( string::iterator cChar = buf3.begin(); cChar != buf3.end(); ++cChar ) {
		if ( (*cChar) == ColorTable::COLOR_ESCAPE ) {
			if ( ++cChar == buf3.end() ) 
				break;
			buf2 << ColorTable::Substitute( (*cChar) );
		} else {
			buf2 << (*cChar); // There was no color this char.
		}
	} 
	_socket->SetOutput( buf2 );
}

void Avatar::SetDisconnected( bool value ) {
	_disconnected = value;
	_socket->SetDisconnected( true );
}

bool Avatar::IsDisconnected( ) {
	return _disconnected;
}

////////////////////// HANDLERS //////////////////////////
void Avatar::ReplaceHandler( Handler * handler ) {
	PopHandler( );
	StackHandler( handler );
}

void Avatar::StackHandler( Handler * handler ) {
	_handlers.push_front( handler );
	handler->Enter( this );
}

void Avatar::ExitHandler( ) {
	PopHandler( );
	_handlers.front()->Enter( this ) ;
}

void Avatar::PopHandler( ) {
	Handler * previous_handler = _handlers.front();
	_handlers.pop_front( );
	previous_handler->Exit( this );
}

void Avatar::HandleInput( const std::string &input ) {
	string::iterator cChar;
	string string1;
	string string2;
 
	string2 = input;
	for ( cChar = string2.begin(); cChar != string2.end(); ++cChar ) {
		if ( ( (*cChar) == '\n' || (*cChar) == '\r' ) && cChar != string2.begin() ) {
			_handlers.front()->Handle( this, string1 );
			string1.erase();
		} else {
			string1 << (*cChar);
		}
	}
			
	_handlers.front()->Handle( this, string1 );
}
///////////////////// END HANDLERS //////////////////////
